/*
 * Snapmaker2-Controller Firmware
 * Copyright (C) 2019-2020 Snapmaker [https://github.com/Snapmaker]
 *
 * This file is part of Snapmaker2-Controller
 * (see https://github.com/Snapmaker/Snapmaker2-Controller)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "module_base.h"
#include "../../Common/debug/debug.h"
#include "../../Common/protocol_sstp/protocol_sstp.h"
#include "../can_host/can_host.h"
#include "cmsis_os2.h"
#include "sys/_stdint.h"

ModuleBase *static_modules[] = {
    //   &printer_single,
    //   &printer_dualextruder,
    NULL};

bool ModuleBase::lock_marlin_uart_ = false;
LockMarlinUartSource ModuleBase::lock_marlin_source_ = LOCK_SOURCE_NONE;
uint16_t ModuleBase::timer_in_static_process_ = 0;
ModuleToolHeadType ModuleBase::toolhead_ = MODULE_TOOLHEAD_UNKNOW;

ErrCode ModuleBase::Upgrade(MAC_t &mac, uint32_t fw_addr, uint32_t fw_length) {
  ErrCode ret = E_SUCCESS;
  return ret;
}

ErrCode ModuleBase::InitModule8p(MAC_t &mac, int dir_pin, uint8_t index) {
  CanExtCmd_t cmd;
  uint8_t buffer[16];

  cmd.data = buffer;
  cmd.length = 2;
  cmd.mac = mac;

  cmd.data[MODULE_EXT_CMD_INDEX_ID] = MODULE_EXT_CMD_CONFIG_REQ;
  cmd.data[MODULE_EXT_CMD_INDEX_DATA] = index;

  //   WRITE(dir_pin, HIGH);
  vTaskDelay(pdMS_TO_TICKS(10));

  // didn't get ack from module
  if (canhost.SendExtCmdSync(cmd, 500) != E_SUCCESS) {
    LOG_E("InitModule8p error: 0x%08x, %u\n", mac.val, E_HARDWARE);
    return E_HARDWARE;
  }

  // module didn;t detect HIGH in dir pin
  if (cmd.data[MODULE_EXT_CMD_INDEX_DATA] != 1) {
    LOG_E("InitModule8p error: 0x%08x, %u\n", mac.val, E_INVALID_STATE);
    return E_INVALID_STATE;
  }

  //   WRITE(dir_pin, LOW);

  return E_SUCCESS;
}

void ModuleBase::LockMarlinUart(LockMarlinUartSource source) {
  lock_marlin_uart_ = true;
  lock_marlin_source_ = max(lock_marlin_source_, source);
}

void ModuleBase::UnlockMarlinUart() {
  lock_marlin_uart_ = false;
  lock_marlin_source_ = LOCK_SOURCE_NONE;
}

void ModuleBase::ReportMarlinUart() {
  if (!lock_marlin_uart_)
    return;
  switch (lock_marlin_source_) {
  case LOCK_SOURCE_NONE:
  case LOCK_SOURCE_ENCLOSURE:
    //   SERIAL_ECHOLN(";Locked UART");
    break;
  case LOCK_SOURCE_EMERGENCY_STOP:
    break;
  }
}

ErrCode ModuleBase::SetMAC(SSTP_Event_t &event) {
  CanExtCmd_t cmd;
  uint8_t buffer[8];

  int i;
  uint32_t old_mac;

  old_mac = (uint32_t)((event.data)[0] << 24 | (event.data)[1] << 16 |
                       (event.data)[2] << 8 | (event.data)[3]);

  cmd.data = buffer;
  cmd.data[0] = MODULE_EXT_CMD_SSID_REQ;
  cmd.data[1] = 1;
  cmd.data[2] = event.data[4];
  cmd.data[3] = event.data[5];
  cmd.data[4] = event.data[6];
  cmd.data[5] = event.data[7];

  // error code to HMI
  event.data[0] = E_FAILURE;
  event.length = 1;

  old_mac &= MODULE_MAC_ID_MASK;
  for (i = 0; i < MODULE_SUPPORT_CONNECTED_MAX; i++) {
    if (old_mac == (canhost.mac(i) & MODULE_MAC_ID_MASK))
      goto out;
  }

  goto error;

out:
  cmd.mac.val = canhost.mac(i);
  event.data[0] = canhost.SendExtCmdSync(cmd, 500);

error:
  return E_FAILURE;
  //   return hmi.Send(event);
}

ErrCode ModuleBase::GetMAC(SSTP_Event_t &event) {
  int i, j = 0;
  uint32_t tmp;
  uint8_t buffer[4 * MODULE_SUPPORT_CONNECTED_MAX];

  for (i = 0; i < MODULE_SUPPORT_CONNECTED_MAX; i++) {
    if ((tmp = canhost.mac(i)) == MODULE_MAC_ID_INVALID)
      break;

    WORD_TO_PDU_BYTES_INDEX_MOVE(buffer, tmp, j);
  }

  event.data = buffer;
  event.length = (uint16_t)j;

  //   return hmi.Send(event);
  return E_SUCCESS;
}

extern uint32_t GRID_MAX_POINTS_X;
extern uint32_t GRID_MAX_POINTS_Y;
void ModuleBase::SetToolhead(ModuleToolHeadType toolhead) {
  //   bool need_saved = false;

  //   // if plugged non-3DP toolhead, will reset leveling data
  //   if (toolhead != MODULE_TOOLHEAD_3DP && toolhead !=
  //   MODULE_TOOLHEAD_DUALEXTRUDER) {
  //     for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++)
  //       for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) {
  //         if (z_values[x][y] != DEFAUT_LEVELING_HEIGHT && z_values[x][y] !=
  //         DEFAUT_LEVELING_HEIGHT_3DP2E) {
  //           z_values[x][y] = DEFAUT_LEVELING_HEIGHT_3DP2E;
  //           bed_level_virt_interpolate();
  //           need_saved = true;
  //         }
  //       }
  //   }

  //   toolhead_ = toolhead;
  //   set_min_planner_speed();
  //   if (need_saved)
  //     settings.save();
}

void ModuleBase::StaticProcess() {
  //   laser_1_6_w.Process();
  //   enclosure.Process();
  //   emergency_stop.Process();
  //   purifier.Process();
  //   printer_single.Process();
  //   printer_dualextruder.Process();
  //   laser_10w.Process();
  //   laser_20w.Process();
  //   laser_40w.Process();

  if (++timer_in_static_process_ < 100)
    return;
  timer_in_static_process_ = 0;

  ReportMarlinUart();
}

extern ModuleBase *static_modules[];
